﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows;

namespace NanoMvvm
{
    public class ResourceHelper
    {
        private readonly NanoMvvmFramework framework;

        internal ResourceHelper(NanoMvvmFramework frameworkInstance)
        {
            if (frameworkInstance == null)
            {
                throw new ArgumentNullException("frameworkInstance");
            }

            framework = frameworkInstance;
        }

        public static string DefaultXamlResourcesPath
        {
            get
            {
                return "ResourceDictionaries/ResourceLibrary.xaml";
            }
        }

        public void LoadXamlLibrary(Assembly assembly)
        {
            if (assembly == null)
            {
                return;
            }

            var res = LoadXamlResources(assembly, DefaultXamlResourcesPath);

            if (res != null)
            {
                AddResources(res);
            }
        }

        public void LoadXamlLibrary(Assembly assembly, string xamlPath)
        {
            if (assembly == null)
            {
                return;
            }

            var res = LoadXamlResources(assembly, xamlPath);

            if (res != null)
            {
                AddResources(res);
            }
        }

        internal void LoadLocalXamlLibrary()
        {
            var res = LoadXamlResources(typeof(ResourceHelper).Assembly, DefaultXamlResourcesPath);

            if (res != null)
            {
                AddResources(res);
            }
        }

        internal IList<T> FindResources<T>()
        {
            var result = new List<T>();

            if (RunModeResolver.InDesignMode)
            {
                return result;
            }

            try
            {
                result = FindResources<T>(framework.Application.Resources).ToList();
            }
            catch (Exception ex)
            {
                Trace.WriteLine("Resources not found: " + ex.Message);
            }

            return result;
        }

        private static ResourceDictionary LoadXamlResources(Assembly assembly, string xamlPath)
        {
            if (assembly == null)
            {
                throw new ArgumentNullException("assembly");
            }

            if (string.IsNullOrWhiteSpace(xamlPath))
            {
                throw new ArgumentException("xamlPath empty");
            }

            var resLibrary = new ResourceDictionary();

            xamlPath = xamlPath.TrimStart('/');

            string source = @"pack://application:,,,/" + assembly.GetName().Name + ";component/" + xamlPath;

            try
            {
                resLibrary.Source = new Uri(source, UriKind.RelativeOrAbsolute);
            }
            catch (IOException)
            {
                return null;
            }

            return resLibrary;
        }

        private void AddResources(ResourceDictionary dict)
        {
            if (RunModeResolver.InDesignMode)
            {
                return;
            }

            if (framework.Application.Resources.MergedDictionaries.All(d => d.Source != dict.Source))
            {
                framework.Application.Resources.MergedDictionaries.Add(dict);
            }
        }

        private IEnumerable<T> FindResources<T>(ResourceDictionary dict)
        {
            var found = dict.Values.OfType<T>();

            return dict.MergedDictionaries.Aggregate(found, (current, dict1) => current.Concat(FindResources<T>(dict1)));
        }

    }
}